/*-------------------------------------------------------------------------
 *
 * nodeValuesscan.c
 *	  扫描值列表（rangetable中的"VALUES (...), (...), ..."）的支持例程。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/executor/nodeValuesscan.c
 *
 *-------------------------------------------------------------------------
 */
/*
 * 接口例程
 *		ExecValuesScan			扫描值列表。
 *		ExecValuesNext			以顺序检索下一个元组。
 *		ExecInitValuesScan		创建并初始化一个值扫描节点。
 *		ExecEndValuesScan		释放任何分配的存储。
 *		ExecReScanValuesScan	重新扫描值列表
 */
#include "postgres.h"

#include "executor/executor.h"
#include "executor/nodeValuesscan.h"
#include "jit/jit.h"
#include "optimizer/clauses.h"
#include "utils/expandeddatum.h"


static TupleTableSlot *fc_ValuesNext(ValuesScanState *fc_node);


/* ----------------------------------------------------------------
 *						扫描支持
 * ----------------------------------------------------------------
 */

/* ----------------------------------------------------------------
 *		ValuesNext
 *
 *		这是ExecValuesScan的工作马
 * ----------------------------------------------------------------
 */
static TupleTableSlot * fc_ValuesNext(ValuesScanState *fc_node)
{
	TupleTableSlot *fc_slot;
	EState	   *fc_estate;
	ExprContext *fc_econtext;
	ScanDirection fc_direction;
	int			fc_curr_idx;

	/*
	 * 从estate和扫描状态获取信息
	 */
	fc_estate = fc_node->ss.ps.state;
	fc_direction = fc_estate->es_direction;
	fc_slot = fc_node->ss.ss_ScanTupleSlot;
	fc_econtext = fc_node->rowcontext;

	/*
	 * 获取下一个元组。如果没有更多元组，则返回NULL。
	 */
	if (ScanDirectionIsForward(fc_direction))
	{
		if (fc_node->curr_idx < fc_node->array_len)
			fc_node->curr_idx++;
	}
	else
	{
		if (fc_node->curr_idx >= 0)
			fc_node->curr_idx--;
	}

	/*
	 * 始终清除结果槽；如果我们在数据的末尾，这样做是适当的， 如果没有，我们在存储虚拟元组协议的第一步中仍然需要它。
	 * 在重置它可能包含指针的上下文之前清除槽似乎是明智的。
	 */
	ExecClearTuple(fc_slot);

	fc_curr_idx = fc_node->curr_idx;
	if (fc_curr_idx >= 0 && fc_curr_idx < fc_node->array_len)
	{
		List	   *fc_exprlist = fc_node->exprlists[fc_curr_idx];
		List	   *fc_exprstatelist = fc_node->exprstatelists[fc_curr_idx];
		MemoryContext fc_oldContext;
		Datum	   *fc_values;
		bool	   *fc_isnull;
		ListCell   *fc_lc;
		int			fc_resind;

		/*
		 * 去除任何之前周期的残留物。我们使用ReScanExprContext
		 * 而不仅仅是ResetExprContext，因为我们希望调用任何注册的关闭
		 * 回调。
		 */
		ReScanExprContext(fc_econtext);

		/*
		 * 在每个元组上下文中执行每个VALUES行的工作。
		 */
		fc_oldContext = MemoryContextSwitchTo(fc_econtext->ecxt_per_tuple_memory);

		/*
		 * 除非我们已经为这一行生成了表达式评估状态，
		 * 否则在econtext的每个元组内存中构建它。这有点
		 * 不寻常，但我们希望在移动到下一行时再次删除
		 * 评估状态，以避免在长值列表中内存需求的增长。
		 * 对于不会工作的行，我们已经在计划启动时构建了评估状态。
		 */
		if (fc_exprstatelist == NIL)
		{
			/*
			 * 将父级作为NULL传递，而不是我的计划节点，因为我们不希望
			 * 这个临时状态中的任何内容链接到永久状态。
			 * 唯一可能希望这样做的表达式类型是SubPlan，
			 * 并且我们已经检查过没有任何。
			 *
			 * 注意，传递parent = NULL也禁用了表达式的JIT编译，
			 * 这是一个优势，因为在正常情况下它们只会使用一次。
			 */
			fc_exprstatelist = ExecInitExprList(fc_exprlist, NULL);
		}

		/* 解析器应该检查所有子列表的长度相同 */
		Assert(list_length(fc_exprstatelist) == fc_slot->tts_tupleDescriptor->natts);

		/*
		 * 计算表达式并构建虚拟结果元组。我们
		 * 已经执行了ExecClearTuple(slot)。
		 */
		fc_values = fc_slot->tts_values;
		fc_isnull = fc_slot->tts_isnull;

		fc_resind = 0;
		foreach(fc_lc, fc_exprstatelist)
		{
			ExprState  *fc_estate = (ExprState *) lfirst(fc_lc);
			Form_pg_attribute fc_attr = TupleDescAttr(fc_slot->tts_tupleDescriptor,
												   fc_resind);

			fc_values[fc_resind] = ExecEvalExpr(fc_estate,
										  fc_econtext,
										  &fc_isnull[fc_resind]);

			/*
			 * 我们必须强制任何R/W扩展数据项为只读状态，以防它们在计划节点的输出
			 * 表达式中被多次引用，或者在我们跳过输出投影且输出
			 * 列在更高计划节点中被多次引用的情况下。
			 */
			fc_values[fc_resind] = MakeExpandedObjectReadOnly(fc_values[fc_resind],
														fc_isnull[fc_resind],
														fc_attr->attlen);

			fc_resind++;
		}

		MemoryContextSwitchTo(fc_oldContext);

		/*
		 * 并返回虚拟元组。
		 */
		ExecStoreVirtualTuple(fc_slot);
	}

	return fc_slot;
}

/*
 * ValuesRecheck -- 访问方法例程以在EvalPlanQual中重新检查元组
 */
static bool fc_ValuesRecheck(ValuesScanState *fc_node, TupleTableSlot *fc_slot)
{
	
	return true;
}



/**************************************************** 
 *		ExecValuesScan(node)
 *
 *		按顺序扫描值列表并返回下一个符合条件的元组。
 *		我们调用 ExecScan() 例程并传递适当的
 *		访问方法函数。
 **************************************************** 
*/
static TupleTableSlot * fc_ExecValuesScan(PlanState *fc_pstate)
{
	ValuesScanState *fc_node = castNode(ValuesScanState, fc_pstate);

	return ExecScan(&fc_node->ss,
					(ExecScanAccessMtd) fc_ValuesNext,
					(ExecScanRecheckMtd) fc_ValuesRecheck);
}

/* ---------------------------------------------------------------
 *		ExecInitValuesScan
 * ---------------------------------------------------------------
 */
ValuesScanState *
ExecInitValuesScan(ValuesScan *fc_node, EState *fc_estate, int fc_eflags)
{
	ValuesScanState *fc_scanstate;
	TupleDesc	fc_tupdesc;
	ListCell   *fc_vtl;
	int			fc_i;
	PlanState  *fc_planstate;

	/*
	 * ValuesScan 不应该有任何子节点。
	 */
	Assert(outerPlan(fc_node) == NULL);
	Assert(innerPlan(fc_node) == NULL);

	/*
	 * 为节点创建新的 ScanState
	 */
	fc_scanstate = makeNode(ValuesScanState);
	fc_scanstate->ss.ps.plan = (Plan *) fc_node;
	fc_scanstate->ss.ps.state = fc_estate;
	fc_scanstate->ss.ps.ExecProcNode = fc_ExecValuesScan;

	/*
	 * 杂项初始化
	 */
	fc_planstate = &fc_scanstate->ss.ps;

	/*
	 * 创建表达式上下文。我们需要两个，一个用于每个子列表
	 * 处理，一个供 execScan.c 用于资格和投影。我们
	 * 通过使用 ExecAssignExprContext() 来构建两个上下文，稍微作弊了一下。
	 */
	ExecAssignExprContext(fc_estate, fc_planstate);
	fc_scanstate->rowcontext = fc_planstate->ps_ExprContext;
	ExecAssignExprContext(fc_estate, fc_planstate);

	/*
	 * 获取值列表的信息，使用它初始化扫描槽。
	 */
	fc_tupdesc = ExecTypeFromExprList((List *) linitial(fc_node->values_lists));
	ExecInitScanTupleSlot(fc_estate, &fc_scanstate->ss, fc_tupdesc, &TTSOpsVirtual);

	/*
	 * 初始化结果类型和投影。
	 */
	ExecInitResultTypeTL(&fc_scanstate->ss.ps);
	ExecAssignScanProjectionInfo(&fc_scanstate->ss);

	/*
	 * 初始化子表达式
	 */
	fc_scanstate->ss.ps.qual =
		ExecInitQual(fc_node->scan.plan.qual, (PlanState *) fc_scanstate);

	/*
	 * 其他节点特定的设置
	 */
	fc_scanstate->curr_idx = -1;
	fc_scanstate->array_len = list_length(fc_node->values_lists);

	/*
	 * 将表达式子列表转换为数组，便于在运行时进行寻址。
	 * 同时，检测任何子列表是否包含 SubPlans；对于这些子列表，继续进行表达式初始化。
	 * （这避免了 SubPlans 想要连接到外部计划树的问题。特别是，如果没有进行此操作，EXPLAIN 将无法看到子计划；
	 * 此外，如果尝试将 SubPlans 处理为简单表达式，我们将遇到悬空指针和/或资源泄露的问题。）
	 */
	fc_scanstate->exprlists = (List **)
		palloc(fc_scanstate->array_len * sizeof(List *));
	fc_scanstate->exprstatelists = (List **)
		palloc0(fc_scanstate->array_len * sizeof(List *));
	fc_i = 0;
	foreach(fc_vtl, fc_node->values_lists)
	{
		List	   *fc_exprs = lfirst_node(List, fc_vtl);

		fc_scanstate->exprlists[fc_i] = fc_exprs;

		/*
		 * 在简单情况下，如果没有任何 SubPlans，可以避免 contain_subplans() 扫描的成本。
		 */
		if (fc_estate->es_subplanstates &&
			contain_subplans((Node *) fc_exprs))
		{
			int			fc_saved_jit_flags;

			/*
			 * 由于这些表达式仅使用一次，因此禁用 JIT。
			 * 这样做是有价值的，因为通常通过 VALUES() 插入大量数据。
			 * 注意，这并不妨碍在子计划 *内* 使用 JIT，因为那是单独初始化的；
			 * 这只是影响上层子表达式。
			 */
			fc_saved_jit_flags = fc_estate->es_jit_flags;
			fc_estate->es_jit_flags = PGJIT_NONE;

			fc_scanstate->exprstatelists[fc_i] = ExecInitExprList(fc_exprs,
															&fc_scanstate->ss.ps);

			fc_estate->es_jit_flags = fc_saved_jit_flags;
		}
		fc_i++;
	}

	return fc_scanstate;
}

/* ----------------------------------------------------------------
 *		ExecEndValuesScan
 *
 *		释放通过 C 程序分配的任何存储。
 * ----------------------------------------------------------------
 */
void ExecEndValuesScan(ValuesScanState *fc_node)
{
	/*
	 * 释放两个 exprcontexts
	 */
	ExecFreeExprContext(&fc_node->ss.ps);
	fc_node->ss.ps.ps_ExprContext = fc_node->rowcontext;
	ExecFreeExprContext(&fc_node->ss.ps);

	/*
	 * 清空元组表
	 */
	if (fc_node->ss.ps.ps_ResultTupleSlot)
		ExecClearTuple(fc_node->ss.ps.ps_ResultTupleSlot);
	ExecClearTuple(fc_node->ss.ss_ScanTupleSlot);
}

/* ----------------------------------------------------------------
 *		ExecReScanValuesScan
 *
 *		重新扫描关系。
 * ----------------------------------------------------------------
 */
void ExecReScanValuesScan(ValuesScanState *fc_node)
{
	if (fc_node->ss.ps.ps_ResultTupleSlot)
		ExecClearTuple(fc_node->ss.ps.ps_ResultTupleSlot);

	ExecScanReScan(&fc_node->ss);

	fc_node->curr_idx = -1;
}
